package br.ufal.tci.nexos.arcolive.beans;

import java.beans.Introspector;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Serializable;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Vector;

/**
 * @author <a href="mailto:felipe@labpesquisas.tci.ufal.br">Felipe Barros Pontes</a>.
 * @author <a href="mailto:leandro@labpesquisas.tci.ufal.br">Leandro Melo de
 *         Sales</a>.
 */
public class Connection implements Runnable, Serializable {
	public static final String PROP_ACTIVE_PROPERTY = "activeProperty";

	private boolean activeProperty;

	public static final String PROP_REMOTEHOST_PROPERTY = "remoteHostProperty";

	private String remoteHostProperty;

	public static final String PROP_REMOTEPORT_PROPERTY = "remotePortProperty";

	private int remotePortProperty;

	public static final String PROP_LISTENING_PROPERTY = "listeningProperty";

	private boolean listening;

	private Socket socket;

	private BufferedReader in;

	private PrintWriter out;

	private Thread runner;

	private Vector ArCoLIVEConnectionListeners = new Vector();

	private PropertyChangeSupport propertySupport;

	/**
	 * 
	 */
	public Connection() {
		this.propertySupport = new PropertyChangeSupport(this);
	}

	/**
	 * @return
	 */
	public boolean isActive() {
		return this.activeProperty;
	}

	/**
	 * @param value
	 * @throws CannotSetActiveConnectionException
	 */
	public void setActive(boolean value) throws CannotSetActiveConnectionException {
		String message = "Cannot set active connection: ";
		boolean oldValue = this.activeProperty;
		this.activeProperty = value;
		try {
			this.open();
		} catch (CannotOpenConnectionException e) {
			throw new CannotSetActiveConnectionException(message + e.getMessage());
		}
		this.propertySupport.firePropertyChange(PROP_ACTIVE_PROPERTY, oldValue,
				this.activeProperty);
	}

	/**
	 * @return
	 */
	public boolean isListening() {
		return this.listening;
	}

	/**
	 * @throws CannotOpenConnectionException
	 */
	public void open() throws CannotOpenConnectionException {
		String message = "Cannot open connection: ";
		try {
			this.socket = new Socket(this.remoteHostProperty,
					this.remotePortProperty);
			this.in = new BufferedReader(new InputStreamReader(this.socket
					.getInputStream()));
			this.out = new PrintWriter(socket.getOutputStream(), true);
		} catch (UnknownHostException e) {
			throw new CannotOpenConnectionException(message + e.getMessage());
		} catch (IOException e) {
			throw new CannotOpenConnectionException(message + e.getMessage());
		}
	}

	/**
	 * @return
	 */
	public String getRemoteHostProperty() {
		return this.remoteHostProperty;
	}

	/**
	 * @param value
	 */
	public void setRemoteHostProperty(String value) {
		String oldValue = this.remoteHostProperty;
		this.remoteHostProperty = value;
		this.propertySupport.firePropertyChange(PROP_ACTIVE_PROPERTY, oldValue,
				this.remoteHostProperty);
	}

	/**
	 * @return
	 */
	public int getRemotePortProperty() {
		return this.remotePortProperty;
	}

	/**
	 * @param value
	 */
	public void setRemotePortProperty(int value) {
		int oldValue = this.remotePortProperty;
		this.remotePortProperty = value;
		this.propertySupport.firePropertyChange(PROP_ACTIVE_PROPERTY, oldValue,
				this.remotePortProperty);
	}

	/**
	 * 
	 */
	public void receiveLn() {
		if (this.runner == null) {
			this.runner = new Thread(this);
			this.runner.start();
		}
	}

	/**
	 * @param message
	 */
	public void sendLn(String message) {
		this.out.println(message);
	}

	/**
	 * 
	 */
	public void stopListen() {
		if (this.runner != null) {
			runner.interrupt();
			runner = null;
		}
	}

	/**
	 * @param event
	 */
	public void receiveSocketEvent(ConnectionEvent event) {

	}

	/**
	 * @param tCPClientConnectionListener
	 */
	public synchronized void addArCoLIVEConnectionListener(
			ConnectionListener tCPClientConnectionListener) {
		this.ArCoLIVEConnectionListeners.add(tCPClientConnectionListener);
	}

	/**
	 * @param tCPClientConnectionListener
	 */
	public synchronized void removeArCoLIVEConnectionListener(
			ConnectionListener tCPClientConnectionListener) {
		this.ArCoLIVEConnectionListeners
				.removeElement(tCPClientConnectionListener);
	}

	/**
	 * @param listener
	 */
	public void addPropertyChangeListener(PropertyChangeListener listener) {
		propertySupport.addPropertyChangeListener(listener);
	}

	/**
	 * @param listener
	 */
	public void removePropertyChangeListener(PropertyChangeListener listener) {
		propertySupport.removePropertyChangeListener(listener);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		String inputLine;
		this.listening = true;
		while (!Thread.interrupted()) {
			try {
				if ((inputLine = this.in.readLine()) != null) {
					this.fireReceiveLnEvent(new ConnectionEvent(this,
							inputLine));
				}
				this.listening = false;
			} catch (IOException ioe) {
				System.out.println("Error while trying to listen server at "
						+ this.remoteHostProperty + ":"
						+ this.remotePortProperty);
			}
		}
	}

	/**
	 * @param event
	 */
	public void fireReceiveLnEvent(ConnectionEvent event) {
		Vector currentListeners = null;
		synchronized (this) {
			currentListeners = (Vector) this.ArCoLIVEConnectionListeners
					.clone();
		}

		for (int i = 0; i < currentListeners.size(); i++) {
			((ConnectionListener) currentListeners.elementAt(i))
					.receiveSocketEvent(event);
		}
	}

}